package com.maslyk.invitation.entitybean;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PostPersist;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import com.maslyk.configuration.ConfigurationManager;
import com.maslyk.invitation.audit.ObjectName;
import com.maslyk.invitation.audit.PropertyLabel;
import com.maslyk.invitation.entitybean.abstrct.GenericEntity;
import com.maslyk.invitation.utils.StringUtils;

/**
 * 
 * @author kris
 */
@Entity
@Table(name = "attachment")
@ObjectName(label="Załącznik")
public class Attachment extends GenericEntity
{
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Basic(optional = false)
	@Column(name = "id")
	private Integer id;
	@Basic(optional = false)
	@Column(name = "name")
	@PropertyLabel(label="Nazwa pliku")
	private String name;
	/*
	@Basic(optional = false, fetch = FetchType.LAZY)
	@Lob
	@Column(name = "data")
	*/
	@Transient
	private byte[] data;
	@JoinColumn(name = "orderid", referencedColumnName = "id")
	@ManyToOne(optional = false, fetch = FetchType.LAZY)
	private Order orderid;
	@Column(name = "creationDate")
	@Temporal(TemporalType.TIMESTAMP)
	@PropertyLabel(label="Data utworzenia")
	private Date creationDate = new Date();

	public Attachment()
	{
	}

	public Attachment(Integer id)
	{
		this.id = id;
	}

	public Attachment(Integer id, String name, byte[] file, Order order)
	{
		this.id = id;
		this.name = name;
		this.data = file;
		this.orderid = order;
	}

	public Integer getId()
	{
		return id;
	}

	public void setId(Integer id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public byte[] getData() throws Exception
	{
		onPostLoad();
		return data;
	}

	public void setData(byte[] data)
	{
		this.data = data;
	}

	public long getCapacity() throws Exception
	{
		getData();
		
		if(data == null)
			return 0;
		
		return data.length;
	}

	public Order getOrder()
	{
		return orderid;
	}

	public void setOrder(Order order)
	{
		this.orderid = order;
	}

	@Override
	public int hashCode()
	{
		int hash = 0;
		hash += (id != null ? id.hashCode() : 0);
		return hash;
	}

	@Override
	public boolean equals(Object object)
	{
		if (!(object instanceof Attachment))
			return false;

		Attachment other = (Attachment) object;
		
		return other.getName().equals(this.name);
	}

	@Override
	public String toString()
	{
		return name;
	}

	public Date getCreationDate()
	{
		return creationDate;
	}

	public void setCreationDate(Date creationDate)
	{
		this.creationDate = creationDate;
	}
	
	/**
	 * Przed utworzeniem atachmentu
	 */
	@PrePersist
	public void onPrePersist()
	{
		this.name = StringUtils.replacePolishSpecialCharacters(name);
	}

	/** zapisanie attachmentu na dysk
	 * 
	 * @throws Exception
	 */
	@PostPersist
	public void onPostPersist() throws Exception
	{
		File thisAttachmentFile = getFullAttachmentFileName();
		
		thisAttachmentFile.createNewFile();
		FileOutputStream fos = new FileOutputStream(thisAttachmentFile);
		
		fos.write(data);
		fos.close();
	}

	void onPostLoad() throws Exception
	{
		// attachment byl wczesniej w bazie
		if(data != null)
			return;
		
		File attachment  = getFullAttachmentFileName();
		
		if(!attachment.exists())
			return;
		
		data = getBytesFromFile(attachment);
	}

	@PreRemove
	void onPreRemove() throws Exception
	{
		File attachment  = getFullAttachmentFileName();
		
		if(!attachment.exists())
			return;
		else
			attachment.delete();
		
		File orderDir = getThisOrderDir();
		
		// jesli usuwamy ostatni zalacznik to zamiatamy po nim katalog nadrzedny
		String[] files = orderDir.list();
		if(files == null || files.length == 0)
			orderDir.delete();
	}
	
	/**
	 * Metoda nie moze zostac uzyta jesli attachment nie jest zapisany w bazie
	 * @return bezwzgledna sciezke do pliku zalacznika nawet jest nie istnieje
	 * @throws IOException
	 */
	private File getFullAttachmentFileName() throws IOException
	{
		File thisOrderDir = getThisOrderDir();
		thisOrderDir.getAbsolutePath();
		
		StringBuffer destination = new StringBuffer();
		destination.append(thisOrderDir.getAbsolutePath());
		destination.append('/');
		destination.append(name);

		return new File(destination.toString());
	}
	
    /**
     * Returns the contents of the file in a byte array
     * @param file File this method should read
     * @return byte[] Returns a byte[] array of the contents of the file
     */
	private static byte[] getBytesFromFile(File file) throws IOException
	{
		InputStream is = new FileInputStream(file);

		// Get the size of the file
		long length = file.length();

		/*
		 * You cannot create an array using a long type. It needs to be an int
		 * type. Before converting to an int type, check to ensure that file is
		 * not loarger than Integer.MAX_VALUE;
		 */
		if (length > Integer.MAX_VALUE)
			throw new IOException("File " + file.getName() + " is too large");

		// Create the byte array to hold the data
		byte[] bytes = new byte[(int) length];

		// Read in the bytes
		int offset = 0;
		int numRead = 0;
		while ((offset < bytes.length) && ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0))
			offset += numRead;

		// Ensure all the bytes have been read in
		if (offset < bytes.length)
			throw new IOException("Could not completely read file "+ file.getName());

		is.close();
		return bytes;
	}
	
	/**
	 * Metoda nie moze zostac uzyta jesli attachment nie jest zapisany w bazie
	 * @return bezwzgledna sciezke do katalogu zamowienia,
	 *  jesli nie ma takiego katalogu jak numer zamowienia to jest tworzony
	 * @throws IOException
	 */
	private File getThisOrderDir() throws IOException
	{
		String globalAttachmentDir = ConfigurationManager.getInstance().getAttachmentDir();
		
		File globalAttachmentDirFile = new File(globalAttachmentDir);
		
		// utworzenie katalogu glownego za zalacznikow jesli nie istnieje
		if(!globalAttachmentDirFile.exists())
			globalAttachmentDirFile.mkdirs();

		StringBuffer destination = new StringBuffer();
		destination.append(globalAttachmentDir);
		destination.append('/');
		destination.append(getOrder().getNumber().replace('/', '.'));
		
		File orderDir = new File(destination.toString());
		
		if(!orderDir.exists())
			orderDir.mkdirs();
		
		return orderDir;
	}

	public static String getBeanName()
	{
		return "attachmentBean";
	}

	public String getKey() {
		return getOrder().getNumber();
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		Attachment a = (Attachment) super.clone();
		a.setId(null);
		return a;
	}
}